package lk.icta.common.web;


import lk.icta.common.AppConstants;
import lk.icta.common.dao.DistrictDAO;
import lk.icta.core.entity.*;
import lk.icta.core.service.*;
import lk.icta.user.common.Constant;
import lk.icta.user.entity.User;
import lk.icta.user.service.UserManager;
import org.codehaus.jackson.map.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.*;


/**
 * @author Kusal Samarakoon
 */
public class JSONCalenderLookupService extends HttpServlet {

    private static final Logger logger = LoggerFactory.getLogger(JSONCalenderLookupService.class);

    private CalenderScheduleManagementService calenderScheduleSvc;
    private OrganisationManagementService organisationManagementService;
    //todo: remove this service
    private ScheduleManagementService scheduleManagementService;
    private AppointmentScheduleManagementService appointmentScheduleManagementService;
    private LandApplicationManagementService landApplicationManagementService;
    private UserManager userManager;
    private DistrictDAO districtDAO;
    private final ObjectMapper mapper = new ObjectMapper();

    //todo: make these fiels private
    String calenderId;
    String title;
    String description;
    String location;
    String startDate;
    String endDate;
    String mapUser;


    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        WebApplicationContext context = WebApplicationContextUtils.getRequiredWebApplicationContext(config.getServletContext());
        calenderScheduleSvc = (CalenderScheduleManagementService) context.getBean("calendarManagementService");
        organisationManagementService = (OrganisationManagementService) context.getBean("organisationManagementService");
        userManager = (UserManager) context.getBean("userManager");
        scheduleManagementService = (ScheduleManagementService) context.getBean("scheduleManagementService");
        districtDAO = (DistrictDAO) context.getBean("districtDAO");
        appointmentScheduleManagementService = (AppointmentScheduleManagementService) context.getBean("appointmentScheduleManagementService");
        landApplicationManagementService = (LandApplicationManagementService) context.getBean("landApplicationManagementService");

    }

    public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        doGet(request, response);
    }

    public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

        String mode = request.getParameter(WebConstants.MODE);
        String type = request.getParameter(WebConstants.TYPE);
        String data = request.getParameter(WebConstants.CALENDER_DATA);
        String edit = request.getParameter(WebConstants.EDITCALENDER);

        calenderId = request.getParameter("calenderId");
        title = request.getParameter("title");
        description = request.getParameter("description");
        location = request.getParameter("location");
        startDate = request.getParameter("startDate");
        endDate = request.getParameter("endDate");
        mapUser = request.getParameter("mapUser");


        HashMap<String, Object> optionLists;
        try {
            logger.debug("calender lookup");
            HttpSession session = request.getSession(false);
            if (session != null) {
                User user = (User) session.getAttribute(AppConstants.SESSION_USER_BEAN);
                Locale locale = (Locale) session.getAttribute(WebConstants.SESSION_USER_LANG);
                String language = locale.getLanguage();
                optionLists = new HashMap<String, Object>();

                if (type.equals(WebConstants.CALENDER)) {
                    if (mode.equals(WebConstants.SAVE)) {

                        //logger.debug("Calender Lookup save Data===>{}", title);
                        saveCalender(user);
                        //saveFile(data);
                        //createFile();
                        //getJsonString(data);
                        logger.debug("Calender Lookup save : {}", optionLists.size());
                    } else if (mode.equals(WebConstants.EDIT)) {
                        editCalender(calenderId, user);
                        logger.debug("Calender Lookup edit: {}", optionLists.size());


                    } else if (mode.equals(WebConstants.DELETE)) {
                        deleteCalender(calenderId, user);
                        logger.debug("Calender Lookup delete: {}", optionLists.size());


                    } else if (mode.equals(WebConstants.GET_LIST)) {

                        logger.debug("Calender Lookup get User roleId: {}", user.getRole().getId());

                        if (AppConstants.ORMOI_USER_ROLE.equals(user.getRole().getId()) || AppConstants.ORRFL_USER_ROLE.equals(user.getRole().getId())) {

                            if (edit.equals("yes")) {
                                List<CalenderSchedule> calenderSchedules = getAllCalenderScheduleData(user);

                                optionLists.put("allScheduleData", getCalendarScheduleData(calenderSchedules));
                            } else {
                                // List<CalenderSchedule> calenderSchedules = getAllCalenderScheduleData(user);

                                //get Due Date data
                                List<Organization> dueDateData = getDueDateCalenderData(user);
                                List<CalendarData> list1 = getDueDateData(dueDateData, language);

                                //display briefing session data
                                List<CalenderSchedule> calenderSchedules = getAllCalenderScheduleDataByCalenderScheduleType(CalenderScheduleType.BRIEFING_SESSION, user);
                                List<CalendarData> list2 = getCalendarScheduleDataByType(calenderSchedules);

                                List<CalendarData> list = new ArrayList<CalendarData>();
                                if (list1.size() > 0) {
                                    list.addAll(list1);
                                }
                                if (list2.size() > 0) {
                                    list.addAll(list2);
                                }

                                // optionLists.put("allScheduleData", getCalendarScheduleData(calenderSchedules));
                                optionLists.put("allScheduleData", list);

                            }


                            logger.debug("Calender Lookup get data list by ORMOI : {}", optionLists.size());

                        } else if (AppConstants.ORLM_USER_ROLE.equals(user.getRole().getId())) {
                            //get calender data in calender
                            List<CalenderSchedule> calenderSchedules = getAllCalenderScheduleDataByUser(user);

                            //get data from Schedule table by ORLM
                            List<Schedule> Schedules = getScheduleData(user);


                            List<CalendarData> list = new ArrayList<CalendarData>();


                            List<CalendarData> list1 = getCalendarScheduleData(calenderSchedules);
                            List<CalendarData> list2 = getScheduleData(Schedules, language);

                            if (list1.size() > 0) {
                                list.addAll(list1);
                            }
                            if (list2.size() > 0) {
                                list.addAll(list2);
                            }


                            optionLists.put("allScheduleData", list);
                            logger.debug("Calender Lookup get data list by ORLM user: {}", optionLists.size());
                        } else if (AppConstants.ORDS_USER_ROLE.equals(user.getRole().getId()) || AppConstants.LO_USER_ROLE.equals(user.getRole().getId())) {
                            //get calender data in calender
                            List<CalenderSchedule> calenderSchedules = getAllCalenderScheduleDataByUser(user);

                            //get data from Schedule table by ORDS
                            List<Schedule> Schedules = getScheduleData(user);

                            //get data from AppointmentSchedule table by ORDS
                            List<AppointmentSchedule> appointmentSchedules = getAppointmentScheduleDataByDSDivision(user);

                            List<CalendarData> list = new ArrayList<CalendarData>();


                            List<CalendarData> list1 = getCalendarScheduleData(calenderSchedules);
                            List<CalendarData> list2 = getScheduleData(Schedules, language);
                            List<CalendarData> list3 = getAppointmentScheduleData(appointmentSchedules, language);

                            if (list1.size() > 0) {
                                list.addAll(list1);
                            }
                            if (list2.size() > 0) {
                                list.addAll(list2);
                            }
                            if (list3.size() > 0) {
                                list.addAll(list3);
                            }

                            optionLists.put("allScheduleData", list);
                            logger.debug("Calender Lookup get data list by ORDS and LO user: {}", optionLists.size());

                        } else if (AppConstants.ORPII_USER_ROLE.equals(user.getRole().getId())) {
                            //display briefing session data
                            List<CalenderSchedule> calenderSchedules = getAllCalenderScheduleDataByCalenderScheduleType(CalenderScheduleType.BRIEFING_SESSION, user);

                            optionLists.put("allScheduleData", getCalendarScheduleDataByType(calenderSchedules));

                        } else if (AppConstants.SLC_USER_ROLE.equals(user.getRole().getId())) {

                            //get data from AppointmentSchedule table by SLC
                            List<AppointmentSchedule> appointmentSchedules = getAppointmentScheduleDataByApplicant(user);


                            optionLists.put("allScheduleData", getAppointmentScheduleData(appointmentSchedules, language));

                        } else {
                            //get calender data
                            List<CalenderSchedule> calenderSchedules = getAllCalenderScheduleDataByUser(user);

                            List<CalendarData> list = new ArrayList<CalendarData>();


                            List<CalendarData> list1 = getCalendarScheduleData(calenderSchedules);


                            if (list1.size() > 0) {
                                list.addAll(list1);
                            }


                            optionLists.put("allScheduleData", list);
                            logger.debug("Calender Lookup get data list by other user: {}", optionLists.size());

                        }


                    } else if (mode.equals(WebConstants.ORGANIZATION_DATA)) {
                        Map<Integer, String> organizationMap = getOrganizationMap(user, language);
                        optionLists.put("organizationData", getOrganizationList(organizationMap));
                        logger.debug("Calender Lookup get organization data list : {}", optionLists.size());
                    }
                }
            } else {
                logger.warn("User not logged in");
                return;
            }

        } catch (Exception e) {
            logger.error("Fatal Error", e);
            e.printStackTrace();
            return;
        }

        response.setContentType("application/json; charset=utf-8");
        PrintWriter out = response.getWriter();

        mapper.writeValue(out, optionLists);
        out.flush();
    }

    private void saveCalender(User user) {
        CalenderSchedule calenderSchedule = new CalenderSchedule();

        calenderSchedule.setTitle(title);
        calenderSchedule.setDetails(description);
        calenderSchedule.setLocation(location);
        calenderSchedule.setCalenderId(calenderId);
        //calenderSchedule.setStartDate();
        //calenderSchedule.setEndDate();
        calenderSchedule.setStart_time(startDate);
        calenderSchedule.setEnd_time(endDate);

        /*try {
            if (mapUser != null) {
                calenderSchedule.setOrganization(organisationManagementService.getOrganizationByIdUKey(Integer.parseInt(mapUser), user));
                List<User> userLst = userManager.getORPIIUsersByOrganization(Integer.parseInt(mapUser), user);
                if (userLst != null) {
                    calenderSchedule.setUsers(userLst);
                }

            }
        } catch (Exception e) {
            logger.info("No organization selected");
        }*/
        //add in ORPII users to calender by ORMOI
        /*if (AppConstants.ORMOI_USER_ROLE.equals(user.getRole().getId())) {
            try {
                List<User> userLst = userManager.getUsersByRole(AppConstants.ORPII_USER_ROLE, user);
                if (userLst != null) {
                    calenderSchedule.setUsers(userLst);
                }
            } catch (Exception e) {
                logger.info("No organization selected");
            }

        }*/
        // Add BRIEFING SESSION
        if (AppConstants.ORMOI_USER_ROLE.equals(user.getRole().getId())) {
            calenderSchedule.setCalenderScheduleType(CalenderScheduleType.BRIEFING_SESSION);
        }

        calenderSchedule.setActive(true);

        calenderScheduleSvc.addCalenderSchedule(calenderSchedule, user);

    }

    private void editCalender(String id, User user) {

        CalenderSchedule calenderSchedule = calenderScheduleSvc.getCalenderScheduleById(id, user);

        calenderSchedule.setTitle(title);
        calenderSchedule.setDetails(description);
        calenderSchedule.setLocation(location);
        calenderSchedule.setCalenderId(calenderId);
//        calenderSchedule.setStartDate();
//        calenderSchedule.setEndDate();
        calenderSchedule.setStart_time(startDate);
        calenderSchedule.setEnd_time(endDate);

        /*try {
            if (mapUser != null) {
                calenderSchedule.setOrganization(organisationManagementService.getOrganizationByIdUKey(Integer.parseInt(mapUser), user));

                List<User> userLst = userManager.getORPIIUsersByOrganization(Integer.parseInt(mapUser), user);
                if (userLst != null) {
                    calenderSchedule.setUsers(userLst);
                }

            }
        } catch (Exception e) {
            logger.info("No organization selected");
        }*/
        calenderScheduleSvc.updateCalenderSchedule(calenderSchedule, user);

    }

    private void deleteCalender(String id, User user) {
        /*CalenderSchedule calenderSchedule = calenderScheduleSvc.getCalenderScheduleById(id, user);
        if (calenderSchedule != null) {
            calenderSchedule.setActive(false);
            calenderScheduleSvc.updateCalenderSchedule(calenderSchedule, user);
        }*/
        calenderScheduleSvc.deleteCalenderSchedule(id);
    }

    private List<CalendarData> getCalendarScheduleData(List<CalenderSchedule> calenderSchedule) {
        List<CalendarData> list = new ArrayList<CalendarData>();

        if (calenderSchedule != null) {
            logger.debug("calenderSchedule size:{}", calenderSchedule.size());

            for (CalenderSchedule schedule : calenderSchedule) {
                CalendarData data = new CalendarData();
                data.setStart_date(schedule.getStart_time());
                data.setEnd_date(schedule.getEnd_time());
                data.setText(schedule.getTitle());
//            data.setEnd_date(schedule.getStart_time());
                data.setLocation(schedule.getLocation());
                data.setId(schedule.getCalenderId());

                data.setDetails(schedule.getDetails());

                if (schedule.getOrganization() != null) {
                    data.setMapUser(schedule.getOrganization().getOrganizationNameEn());
                }


                list.add(data);
            }
        }
        return list;
    }

    private List<CalendarData> getCalendarScheduleDataByType(List<CalenderSchedule> calenderSchedule) {
        List<CalendarData> list = new ArrayList<CalendarData>();

        if (calenderSchedule != null) {
            logger.debug("calenderSchedule By Type size:{}", calenderSchedule.size());
            for (CalenderSchedule schedule : calenderSchedule) {
                CalendarData data = new CalendarData();
                data.setStart_date(schedule.getStart_time());
                data.setEnd_date(schedule.getEnd_time());

                data.setText("Briefing session");

                data.setLocation("Task : " + schedule.getTitle() + " ,Location : " + schedule.getLocation());
                list.add(data);
            }
        }
        return list;
    }

    /*private List<CalendarData> getScheduleData(List<Schedule> scheduleList, String language) {
        List<CalendarData> list = new ArrayList<CalendarData>(scheduleList.size());
        Set<Land> land;
        SimpleDateFormat dateYYYYMMDD = new SimpleDateFormat("yyyy-MM-dd");
        for (Schedule schedule : scheduleList) {
            land = schedule.getLandList(); // todo: this can give lazy load error. Move all this business logic to service method
            CalendarData data = new CalendarData();
            data.setStart_date(new StringBuilder(dateYYYYMMDD.format(schedule.getEndDate())).toString() + " 08:00");
            //data.setEnd_date(new StringBuilder(dateYYYYMMDD.format(schedule.getEndDate())).toString() + " 08:00");
            data.setEnd_date(new StringBuilder(dateYYYYMMDD.format(schedule.getEndDate())).toString() + " 08:00");
            for (Land lan : land) {
                data.setText(lan.getNameOfTheLand());
            }
            data.setLocation(schedule.getGnDivision().getGNDivisionNameByLanguage(language));
            data.setTask(schedule.getTask());


            list.add(data);
        }
        return list;
    }*/

    private List<CalendarData> getScheduleData(List<Schedule> scheduleList, String language) {
        List<CalendarData> list = new ArrayList<CalendarData>();
        Set<Land> land;
        SimpleDateFormat dateYYYYMMDD = new SimpleDateFormat("yyyy-MM-dd");
        if (scheduleList != null) {


            for (Schedule schedule : scheduleList) {
                land = schedule.getLandList(); // todo: this can give lazy load error. Move all this business logic to service method
                CalendarData data = new CalendarData();
                data.setStart_date(new StringBuilder(dateYYYYMMDD.format(schedule.getEndDate())).toString() + " 08:00");
                //data.setEnd_date(new StringBuilder(dateYYYYMMDD.format(schedule.getEndDate())).toString() + " 08:00");
                data.setEnd_date(new StringBuilder(dateYYYYMMDD.format(schedule.getEndDate())).toString() + " 08:00");
                String landName = "";
                for (Land lan : land) {
                    landName = lan.getNameOfTheLand();
                }
                data.setLocation("Location: " + schedule.getGnDivision().getGNDivisionNameByLanguage(language) +
                        " , LandName :" + landName +
                        " , Description: " + schedule.getTask());

                data.setText("Land Visit");

                list.add(data);
            }
        }
        return list;
    }

    private List<CalendarData> getAppointmentScheduleData(List<AppointmentSchedule> scheduleList, String language) {

        /* List<CalendarData> list = new ArrayList<CalendarData>(scheduleList.size());*/
        List<CalendarData> list = new ArrayList<CalendarData>();

        SimpleDateFormat dateYYYYMMDD = new SimpleDateFormat("yyyy-MM-dd");
        if (scheduleList != null) {

            for (AppointmentSchedule schedule : scheduleList) {
                CalendarData data = new CalendarData();

                String time = schedule.getAppointmentTime();
                if (time.length() > 0) {
                    time = " " + time;
                } else {
                    time = " 08:00";
                }

                data.setStart_date(new StringBuilder(dateYYYYMMDD.format(schedule.getAppointmentDate())).toString() + time);
                data.setEnd_date(new StringBuilder(dateYYYYMMDD.format(schedule.getAppointmentDate())).toString() + time);


                data.setText("Appointment");
                String description = "";
                String des = schedule.getDescription();
                int attend = schedule.getAttend();
                String nominee = schedule.getNominee();
                description = "Ref No: " + schedule.getApplicationId();
                if (attend == 0) {
                    description += " , DS will not be attended. ";
                    description += ", Nominee: " + nominee;
                }

                description += " , Description: " + des;
                data.setLocation(description);


                list.add(data);
            }
        }
        return list;
    }


    //get data from  CalenderSchedule by ormoi

    private List<CalenderSchedule> getAllCalenderScheduleData(User user) {
        List<CalenderSchedule> calenderSchedulesData = calenderScheduleSvc.getAllCalenderSchedule(user);
        logger.debug("Calender JSON data :{}" + calenderSchedulesData.size());
        return calenderSchedulesData;
    }

    private List<CalenderSchedule> getAllCalenderScheduleDataByUser(User user) {
        List<CalenderSchedule> calenderSchedulesData = calenderScheduleSvc.getAllCalenderScheduleByUser(user);
        logger.debug("Calender JSON data :{}" + calenderSchedulesData.size());
        return calenderSchedulesData;
    }

    private List<CalenderSchedule> getAllCalenderScheduleDataByCalenderScheduleType(CalenderScheduleType scheduleType, User user) {
        List<CalenderSchedule> calenderSchedulesData = calenderScheduleSvc.getAllCalenderScheduleByCalenderScheduleType(scheduleType, user);
        logger.debug("Calender JSON data :{}" + calenderSchedulesData.size());
        return calenderSchedulesData;
    }

    //Due date data
    private List<Organization> getDueDateCalenderData(User user) {
        List<Organization> calenderSchedulesData = organisationManagementService.getUnRegisterOrganizationList(user);
        logger.debug("Calender due date JSON data :{}" + calenderSchedulesData.size());
        return calenderSchedulesData;
    }

    //get Due date Data

    private List<CalendarData> getDueDateData(List<Organization> scheduleList, String language) {
        List<CalendarData> list = new ArrayList<CalendarData>();
        SimpleDateFormat dateYYYYMMDD = new SimpleDateFormat("yyyy-MM-dd");
        if (scheduleList != null) {

            for (Organization organization : scheduleList) {
                CalendarData data = new CalendarData();
                data.setStart_date(new StringBuilder(dateYYYYMMDD.format(organization.getDueDate())).toString() + " 08:00");
                //data.setEnd_date(new StringBuilder(dateYYYYMMDD.format(organization.getDueDate())).toString() + " 08:00");
                data.setEnd_date(new StringBuilder(dateYYYYMMDD.format(organization.getDueDate())).toString() + " 08:00");

                data.setText("Register Org.");
                //data.setText(organization.getOrganizationNameEn());

                //data.setTask(organization.getContactPerson().getContacts().getAddress());
                //data.setDetails(organization.getContactPerson().getName());

                data.setLocation("Org. Name : " + organization.getOrganizationNameEn() + ", Address : " + organization.getContactPerson().getContacts().getAddress());

                list.add(data);
            }
        }
        return list;
    }

    //get data from  Schedule by orlm

    private List<Schedule> getScheduleData(User user) {
        //List<Schedule> SchedulesData = scheduleManagementService.getAllActiveSchedule(Schedule.Status.ACTIVE);

        List<Schedule> SchedulesData = scheduleManagementService.getAllActiveSchedulesByDSDivision(Schedule.Status.ACTIVE, user.getDsDivisions().iterator().next().getId(), user);
        logger.debug("Calender JSON data :{}" + SchedulesData.size());
        return SchedulesData;
    }

    //get data from  AppointmentSchedule by user
    private List<AppointmentSchedule> getAppointmentScheduleDataByDSDivision(User user) {
        try {
            List<AppointmentSchedule> appointmentScheduleData = appointmentScheduleManagementService.getAllAppointmentScheduleByDSDivision(user);
            logger.debug("Calender JSON data :{}" + appointmentScheduleData.size());
            return appointmentScheduleData;
        } catch (Exception ex) {
            return null;
        }

    }

    //get data from  AppointmentSchedule by SLC User
    private List<AppointmentSchedule> getAppointmentScheduleDataByApplicant(User user) {
        try {
            long applicationId = 0;
            List<AppointmentSchedule> appointmentScheduleData = new ArrayList<AppointmentSchedule>();

            List<LandApplication> landApplicationList = landApplicationManagementService.getApplicationsByUser(user);
            if (landApplicationList != null && landApplicationList.size() > 0) {
                logger.debug("AppointmentSchedule SLC :{}" + landApplicationList.size());

                for (LandApplication list : landApplicationList) {
                    applicationId = list.getId();
                    logger.debug("AppID SLC :{}" + applicationId);
                    appointmentScheduleData.addAll(appointmentScheduleManagementService.findSchedulesByApplicationId(user, applicationId));
                }

            }

            logger.debug("Calender JSON data :{}" + appointmentScheduleData.size());
            return appointmentScheduleData;
        } catch (Exception ex) {
            return null;
        }

    }


    //get organization list

    private Map<Integer, String> getOrganizationMap(User user, String language) {
        Map<Integer, String> OrganizationListData = organisationManagementService.getOrganizationListByLanguage(language, user);
        logger.debug("Calender JSON organization data :{}" + OrganizationListData.size());
        return OrganizationListData;
    }

    private List getOrganizationList(Map<Integer, String> map) {
        List<OrganizationData> or = new ArrayList<OrganizationData>();
        for (Map.Entry<Integer, String> e : map.entrySet()) {
            OrganizationData data = new OrganizationData();
            data.setKey(e.getKey().toString());
            data.setLabel(e.getValue());

            /*SelectOption option = new SelectOption();
            option.setOptionValue(e.getKey().toString());
            option.setOptionDisplay(e.getValue());*/
            or.add(data);
        }
        return or;
    }


}

